using System;
using System.Collections.Generic;
using System.IO;
using SQLRunnerLib.Project;
using SQLRunnerLib.Services;
using SQLServices.Log;
using SQLServices.TransMgr;

namespace SQLRunnerLib.Util
{
	public enum SessionLogLevel
	{
		Info,
		Error,
		None
	}

	[Flags]
	public enum ErrorIndicator
	{
		None = 0,
		AppFailure = 1,
		StopExecution = 2
	}

	/// <summary>
	/// Reads the command line arguments and start the application 
	/// command line arguments
	/// -p:[project file path]		Indicates the project file to use
	/// -s:[server name]			SQL Server instance
	/// -d:[database name]			Database
	/// -m:[o|p|s|c|r]              Connection Manager
	/// -t							Trusted connection, default value is TRUE
	/// -u:[user name]
	/// -w:[password]
	/// </summary>
	public class LineParser
	{
        public string ServerName { get; internal set; }
        public string UserName { get; internal set; }
        public string Pwd { get; internal set; }
        public SessionLogLevel LogLevel { get; private set; }
        public string DatabaseName { get; internal set; }
        public bool ShowHelp { get; private set; }
        public ErrorIndicator ErrorLevel { get; private set; }
        public ConnectionMgrType ConnMgrType { get; private set; }
        public bool TrustedConnection { get; internal set; }
        public SQLProject Project { get; private set; }
        public bool IsProjectAvailable { get; private set; }

	    private readonly string[] Options = new[] {"p:", "s:", "d:", "u:", "w:", "e:", "h", "t", "m:"};

		public LineParser(IEnumerable<string> args)
		{
		    ConnMgrType = ConnectionMgrType.PessimistConnMgr;
		    LogLevel = SessionLogLevel.Error;
		    ErrorLevel = ErrorIndicator.None;
		    ShowHelp = false;
		    DatabaseName = string.Empty;
		    Pwd = string.Empty;
		    UserName = string.Empty;
		    ServerName = string.Empty;
		    foreach (string arg in args) {
				string value;
                if (arg.StartsWith("-"))
                    // WI#11396 - Cannot connect to SQL Server if pwd is not set in lower case
                    value = arg.Substring(1); //.ToLower();
                else
                    continue;

				foreach (string option in Options) {
                    // WI#11396 - Cannot connect to SQL Server if pwd is not set in lower case
					if (value.StartsWith(option, StringComparison.OrdinalIgnoreCase)) {
						value = value.Substring(option.Length);
						UpdateFlags(option, value);
						break;
					}
				}
			}
		}

	    private void UpdateFlags(string option, string value) {
			switch (option) {
				case "p:":
					DoProject(value);
					break;
				case "s:":
					ServerName = value;
					break;
				case "d:":
					DatabaseName = value;
					break;
				case "t":
					TrustedConnection = true;
					break;
				case "u:":
					UserName = value;
					break;
				case "w:":
					Pwd = value;
					break;
				case "h":
					ShowHelp = true;
					break;
				case "e:":
					DoError(value);
					break;
                case "m:":
			        DoConnMgr(value);
                    break;
				default:
					ServiceContainer.Logger.LogMessage(string.Format(
						"The {0} option is invalid", option), MsgLevel.Error);
					throw new ApplicationException(string.Format(
						"The {0} option is invalid", option));
			}
		}

	    private void DoConnMgr(string value)
	    {
	        Dictionary<string, ConnectionMgrType> possibleValues = GetPossibleValues();
	        if (!possibleValues.ContainsKey(value.ToLower()))
            {
                ServiceContainer.Logger.LogMessage(string.Format(
                    "The {0} option is invalid for a connection manager", value), MsgLevel.Error);
            }
	        ConnMgrType = possibleValues[value.ToLower()];
	    }

	    private Dictionary<string, ConnectionMgrType> GetPossibleValues()
	    {
	        Dictionary<string, ConnectionMgrType> possibleValues;
	        possibleValues = new Dictionary<string, ConnectionMgrType>();
	        possibleValues.Add("o", ConnectionMgrType.OptimisticConnMgr);
	        possibleValues.Add("p", ConnectionMgrType.PessimistConnMgr);
	        possibleValues.Add("s", ConnectionMgrType.StrictConnMgr);
	        possibleValues.Add("c", ConnectionMgrType.CommitTransactionConnMgr);
	        possibleValues.Add("r", ConnectionMgrType.RollbackTransactionConnMgr);
	        return possibleValues;
	    }

	    private void DoProject(string value) {
			value = value.Replace("'", string.Empty).Replace("\"",string.Empty);
			FileInfo projFile = new FileInfo(value);
			if (!projFile.Exists) {
				string msg = string.Format("Project file [{0}] does not exists", value);
				ServiceContainer.Logger.LogMessage(msg, MsgLevel.Error);
				return;
			}
			try 
            {
				Project = SQLProject.Deserialize(value);
				IsProjectAvailable = true;
			}
			catch {
				string msg = string.Format("Project file [{0}] could not be read", value);
				ServiceContainer.Logger.LogMessage(msg, MsgLevel.Error);
			}
		}

		private void DoError(string value) {
			ErrorIndicator errorvalue = ErrorIndicator.None;
			try 
            {
				int error = int.Parse(value);
				if ( ( error & (int) ErrorIndicator.AppFailure) == (int) ErrorIndicator.AppFailure) {
					errorvalue = ErrorIndicator.AppFailure;
				}
				if ( ( error & (int) ErrorIndicator.StopExecution) == (int) ErrorIndicator.StopExecution) {
					errorvalue |= ErrorIndicator.StopExecution;
				}
			}
			catch (Exception ex) {
				ServiceContainer.Logger.LogMessage("Error parsing Error level: " + ex.Message, MsgLevel.Error);
			}
			ErrorLevel = errorvalue;
		}
	}
}
